// Copyright 2012 Google Inc. All Rights Reserved.

package com.google.superlists.servlet;

import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.services.tasks.Tasks;
import com.google.api.services.tasks.Tasks.Tasklists.Insert;
import com.google.api.services.tasks.Tasks.TasksOperations;
import com.google.api.services.tasks.model.Task;
import com.google.api.services.tasks.model.TaskList;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.superlists.model.ActionResult;
import com.google.superlists.model.SuperList;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Future;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Action handling creation of a new TaskList from a SuperList.
 *
 * @author sven@google.com (Sven Mawson)
 */
public class CreateListAction extends BaseAction {

  private final String data;

  public CreateListAction(
      HttpServlet servlet, HttpServletRequest req, HttpServletResponse resp, String data) {
    super(servlet, req, resp);

    this.data = data;
  }

  @Override
  protected ActionResult handle() throws IOException {
    SuperList list = SuperList.fromJson(data);
    String createdId = createTaskList(list);

    ActionResult result = new ActionResult();
    result.id = createdId;
    result.list = list;
    return result;
  }

  /**
   * Saves a SuperList as a TaskList to Tasks. This will also modify the list
   * by setting the tasklist and taskid fields on the list and items.
   */
  private String createTaskList(SuperList list) throws IOException {
    Tasks tasksService = getTasksService();

    TaskList tasklist = new TaskList();
    tasklist.setTitle(list.name);

    Insert insertAction = tasksService.tasklists().insert(tasklist);
    if (usePartialResponses()) {
      insertAction.setFields("id");
    }

    start("createlist.tasklists.insert()");
    tasklist = insertAction.execute();
    stop("createlist.tasklists.insert()");

    // Copy the tasklist id back to the SuperList.
    list.content.tasklistId = tasklist.getId();

    Map<Integer, TasksOperations.Insert> insertOps = Maps.newLinkedHashMap();
    for (int i = 0; i < list.content.items.size(); i++) {
      Task task = new Task();
      task.setTitle(list.content.items.get(i).name);
      TasksOperations.Insert insert = tasksService.tasks().insert(tasklist.getId(), task);
      if (usePartialResponses()) {
        insert.setFields("id");
      }
      insertOps.put(i, insert);
    }

    Map<Integer, Future<Task>> insertedTasks = Maps.newLinkedHashMap();
    if (useBatch()) {
      BatchRequest batchRequest = tasksService.batch();
      for (Map.Entry<Integer, TasksOperations.Insert> entry : insertOps.entrySet()) {
        FutureCallback<Task> callback = createCallback("createlist.tasks.insertedLength");
        entry.getValue().queue(batchRequest, callback);
        insertedTasks.put(entry.getKey(), callback.getFuture());
      }
      int batchSize = batchRequest.size();
      if (batchSize > 0) {
        String name = "createlist.tasks.batch(" + batchSize + ")";
        start(name);
        batchRequest.execute();
        stop(name);
      }
    } else {
      String name = "createlist.tasks.insert(" + insertOps.size() + ")";
      start(name);
      for (Map.Entry<Integer, TasksOperations.Insert> entry : insertOps.entrySet()) {
        Task inserted = entry.getValue().execute();
        addContentLength("createlist.tasks.insertedLength", inserted.getResponseHeaders());
        addContentLength("response.size", inserted.getResponseHeaders());
        insertedTasks.put(entry.getKey(), Futures.immediateFuture(inserted));
      }
      stop(name);
    }

    // Copy the created task ids back to the superlist items they came from.
    for (Map.Entry<Integer, Future<Task>> entry : insertedTasks.entrySet()) {
      int itemIndex = entry.getKey();
      Task task = Futures.getUnchecked(entry.getValue());
      list.content.items.get(itemIndex).taskId = task.getId();
    }

    return tasklist.getId();
  }
}
